<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>电子签名</title>
    <style>
      .canvas-wrap {
        display: inline-block;
        border: 1px solid #dedede;
        font-size: 0;
      }
    </style>
  </head>
  <body>
    <div>
      <label for="bgColor">背景颜色：</label>
      <input type="color" id="bgColor" value="#ffffff" />
      <label for="fontColor">字体颜色：</label>
      <input type="color" id="fontColor" />
      <button id="reDraw">重绘</button>
      <button id="save">保存签名</button>
    </div>
    <hr />
    <div class="canvas-wrap">
      <canvas id="canvas" width="300" height="200"></canvas>
    </div>
    <hr />

    <script>
      let $bgColor = document.querySelector('#bgColor'); // 背景颜色选择器
      let $fontColor = document.querySelector('#fontColor'); // 字体颜色取色器
      let $reDraw = document.querySelector('#reDraw'); // 重绘按钮
      let $save = document.querySelector('#save'); // 保存签名按钮
      let $preview = document.querySelector('#preview'); // 预览
      let canvas = document.getElementById('canvas');
      let ctx = canvas.getContext('2d');
      // canvas 宽高
      let canvasRect = canvas.getBoundingClientRect();

      let bgColor = '#ffffff', // 背景颜色
        fontColor = '#000000', // 字体颜色
        isDraw = false, // 是否正在绘制手势签名
        points = [], // 保存所有的手势路径点
        perPoints = []; // 每一次绘制路径的集合点

      drawBg(); // 绘制背景

      function drawBg() {
        // 清空画布
        ctx.clearRect(0, 0, canvasRect.width, canvasRect.height);
        // 绘制背景区域
        ctx.fillStyle = bgColor; // 设置填充颜色
        ctx.fillRect(0, 0, canvasRect.width, canvasRect.height);
        ctx.strokeStyle = fontColor; // 设置描边颜色
      }

      // 背景颜色改变
      $bgColor.addEventListener('input', function (e) {
        bgColor = e.target.value;
        drawBg(); // 绘制背景
        // 重新显现之前的线条
        ctx.moveTo(0, 0);
        ctx.lineTo(0, 0);
        ctx.stroke();
      });

      // 字体颜色改变
      $fontColor.addEventListener('input', function (e) {
        fontColor = e.target.value;
        ctx.strokeStyle = fontColor;
        // 重新显现之前的线条
        ctx.moveTo(0, 0);
        ctx.lineTo(0, 0);
        ctx.stroke();
      });

      // 鼠标按下的时候标记正在开始绘制手势路径
      canvas.addEventListener('mousedown', function () {
        perPoints = [];
        isDraw = true;
      });

      // 鼠标滑动的时候，绘制路径
      canvas.addEventListener('mousemove', function (e) {
        e.stopPropagation(); // 阻止事件上传
        if (!isDraw) return;
        // 计算鼠标在 canvas 里的位置
        const x = e.pageX - canvasRect.left;
        const y = e.pageY - canvasRect.top;
        if (perPoints.length === 0) {
          ctx.moveTo(x, y);
        } else {
          ctx.moveTo(
            perPoints[perPoints.length - 1][0],
            perPoints[perPoints.length - 1][1]
          );
          ctx.lineTo(x, y);
          ctx.stroke();
        }
        perPoints.push([x, y]);
      });

      // 鼠标抬起的时候标记结束手势路径的绘制
      canvas.addEventListener('mouseup', function () {
        isDraw = false;
        points.push(perPoints);
        perPoints = [];
      });

      // 滑出边界外后，停止继续滑动
      document.body.addEventListener('mousemove', function () {
        isDraw = false;
        points.push(perPoints);
        perPoints = [];
      });

      // 重绘
      $reDraw.addEventListener('click', function () {
        // 利用 canvas 宽高变化会清除所有内容的原理进行重绘
        canvas.width = canvasRect.width;
        drawBg(); // 重新绘制背景
      });

      // 保存
      $save.addEventListener('click', function () {
        if ($preview == null) {
          $preview = document.createElement('img');
          $preview.src = canvas.toDataURL('image/png');
          $preview.id = 'preview';
          document.body.appendChild($preview);
        } else {
          $preview.src = canvas.toDataURL('image/png');
        }
      });
    </script>
  </body>
</html>
